import { isQueue, _rotate2, _dis, _o, _mid, _scale, _disToSeg, _lerp, _footPoint } from "../math/index.js"
import { sinPoints } from '../algorithm/sin.js'
import { _mirror } from "../math/mirror.js"
import { isObject } from "../utils/index.js"
import { controlPoints } from '../algorithm/controlPoints.js'
import { _rough } from "./rough.js"
import { sierpinskiPoints } from '../algorithm/sierpinski.js'
import { kochPoints } from '../algorithm/koch.js'
import { _traversal } from '../algorithm/traversal.js'
// M = moveto
// L = lineto
// H = horizontal lineto
// V = vertical lineto
// C = curveto
// S = smooth curveto
// Q = quadratic Bézier curve
// T = smooth quadratic Bézier curveto
// A = elliptical Arc
// Z = closepath
const pathCommandMap = {
    7: 'A',
    6: 'C',
    4: 'Q',
    2: 'L'
}
export const _lineTo = (p) => {
    if (p.length === 0) {
        return ""
    }
    else if (isQueue(p)) {
        return _lineTo(p[0]) + (p.length > 0 ? _lineTo(p.slice(1)) : '')
    }
    return ` ${pathCommandMap[p.length] || 'M'}${p.join(" ")}`
}
export const _moveTo = (p) => {
    return `M${p.join(" ")} `
}
const _polylineTo = (points) => {
    return points.map(t => {
        return 'L' + t.join(' ')
    }).join(' ')

}
// 弧形
export const _arcTo = (options) => {
    let {
        sweepFlag = true,    // 1顺时针 0逆时针
        largeArcFlag = false,  //小圆
        xAxisRotation = 0,
        r, to
    } = options
    return 'A' + [r, r, xAxisRotation, largeArcFlag ? 1 : 0, sweepFlag ? 1 : 0, ...to].join(' ')
}

// 折线转曲线 
export const polylineToBezier = (points) => {
    let len = points.length
    if (len === 2) {
        let [p1, p2] = points
        let p = [...p1, ...p2]
        return _lineTo(p)
    }
    else if (len >= 3) {
        let [p1, p2, p3, ...rest] = points
        let p = [...p1, ...p2, ...p3]
        return _lineTo(p) + polylineToBezier(rest)
    }
    return ""
}

export const _bezierCurveTo = ([c1, c2, t]) => {
    return 'C' + [c1[0], c1[1], c2[0], c2[1], t[0], t[1]].join(' ')
}
export const _quadraticCurveTo = ([c, to]) => {
    return 'Q' + [c[0], c[1], to[0], to[1]].join(' ')
}

const _bezierTo = ([from, to], [c1, c2]) => {
    return _bezierCurveTo([c2, c1, to])
}

const _leftBezierTo = ([from, to], [c1, c2]) => {
    return _bezierCurveTo([c1, c2, to])
}

const _doubleBezierTo = ([from, to], [c1, c2]) => {
    return _moveTo(from) + _bezierCurveTo([c1, c2, to]) + _moveTo(from) + _bezierCurveTo([c2, c1, to])
}


const _sawtoothTo = ([from, to], [c1, c2]) => {
    return _polylineTo([c2, c1, to])
}
const _leftSawtoothTo = ([from, to], [c1, c2]) => {
    return _polylineTo([c1, c2, to])
}
const _doubleSawtoothTo = ([from, to], [c1, c2]) => {
    return _moveTo(from) + _polylineTo([c1, c2, to]) + _moveTo(from) + _polylineTo([c2, c1, to])
}

const _rightAngleTo = ([from, to], [c1, c2]) => {
    return _polylineTo([c2, to])
}
const _leftAngleTo = ([from, to], [c1, c2]) => {
    return _polylineTo([c1, to])
}
const _diamondTo = ([from, to], [c1, c2]) => {
    return _moveTo(from) + ' ' + _polylineTo([c1, to, c2, from])
}
const _crossTo = ([from, to], [c1, c2]) => {
    return [_moveTo(from),
    _lineTo(to),
    _moveTo(c1),
    _lineTo(c2)].join(' ')
}
const _triangleTo = ([from, to], [c1, c2]) => {
    return _moveTo(from) + _polylineTo([c2, to, from])
}
const _leftTriangleTo = ([from, to], [c1, c2]) => {
    return _moveTo(from) + _polylineTo([c1, to, from])
}
const _circleByRadius = (o, r) => {
    let [x, y] = o
    // let r = _disToSeg(o, [p1, p2])
    //非数值单位计算，如当宽度像100%则移除 
    if (isNaN(x - y)) return '';
    return 'M' + (x - r) + ' ' + y + 'a' + r + ' ' + r + ' 0 1 0 ' + 2 * r + ' 0' + 'a' + r + ' ' + r + ' 0 1 0 ' + (-2 * r) + ' 0' + 'z';

}
const _circleTo = ([from, to]) => {
    let o = _o([from, to])
    let r = _dis(o, to)
    return _circleByRadius(o, r)
}

// 相切圆
const _circleToSeg = (o, [p1, p2]) => {
    let r = _disToSeg(o, [p1, p2])
    return _circleByRadius(o, r)
}

// 半圆
const _semicircleTo = ([from, to]) => {
    let xAxisRotation = 0,
        largeArcFlag = false,
        sweepFlag = false
    let r = _dis(from, to) / 2
    return 'A' + [r, r, xAxisRotation, largeArcFlag ? 1 : 0, sweepFlag ? 1 : 0, ...to].join(' ')
}

const _leftSemicircleTo = ([from, to]) => {
    let xAxisRotation = 0,
        largeArcFlag = false,
        sweepFlag = true
    let r = _dis(from, to) / 2
    return 'A' + [r, r, xAxisRotation, largeArcFlag ? 1 : 0, sweepFlag ? 1 : 0, ...to].join(' ')
}

const _circle = (options) => {
    return _loop(options, _circleTo)

}
// 太极
export const _taichiTo = ([from, to]) => {
    let o = _mid(from, to)
    let o1 = _mid(from, o)
    let o2 = _mid(o, to)
    let r1 = _dis(from, o1) / 2
    return _moveTo(from) + _semicircleTo([from, o]) + _leftSemicircleTo([o, to]) +
        _circleTo([from, to]) + _circleByRadius(o1, r1) + _circleByRadius(o2, r1)
}

export const _taichiToSeg = (o, [p1, p2]) => {
    let fp = _footPoint(o, [p1, p2])
    let o1 = _mid(fp, o)
    let o2 = _mirror(o1, o)
    let r = _dis(o, fp)
    let r1 = r / 4
    let fp2 = _mirror(fp, o)
    return _moveTo(fp) + _semicircleTo([fp, o]) +
        _leftSemicircleTo([o, fp2]) +
        _circleByRadius(o, r) +
        _circleByRadius(o1, r1) + _circleByRadius(o2, r1)
}


const _monkeyToSeg = (o, [p1, p2]) => {
    let fp = _footPoint(o, [p1, p2])
    let o1 = _mid(fp, o)
    let o2 = _mirror(o1, o)
    let r = _dis(o, fp)
    let r1 = r / 4
    let fp2 = _mirror(fp, o)
    return _moveTo(fp) + _semicircleTo([fp, o]) + _semicircleTo([o, fp2]) +
        _circleByRadius(o, r) +
        _circleByRadius(o1, r1) + _circleByRadius(o2, r1)
}




// 循环
export const _loop = ({ points, loop, step = 1, skew = 0, amplitude = 1, depth = 1 }, fn) => {
    let len = points.length
    let prev
    let result = []
    let index = 0
    // let controls = []
    let callFn = (i, t, next) => {
        if (step > 1) {
            if (i % step === 0) {
                result[result.length] = _moveTo(t)
                prev = t
            } else {
                let [c1, c2] = controlPoints([prev, t], skew, amplitude)
                result[result.length] = fn([prev, t], [c1, c2], index++)
            }
        } else {
            let [c1, c2] = controlPoints([t, next], skew, amplitude)
            result[result.length] = fn([t, next], [c1, c2], index++)
        }
    }

    let total = loop ? len : len - 1
    for (let i = 0; i < total; i++) {
        let t = points[i]
        let next = points[(i + 1) % len]
        if (depth === 1) {
            callFn(i, t, next)
        } else {
            let curr = t
            for (let j = 1; j <= depth; j++) {
                let p = _lerp(t, next, j / depth)
                callFn(i, curr, p)
                curr = p
            }
        }

    }
    return result.join(' ')
}

// 曲线
const _bezier = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _bezierTo)
    // return _traversal({
    //     // points,
    //     ...options,
    //     iter:_bezierTo,
    //     init:_moveTo
    // })

}
const _leftBezier = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _leftBezierTo)
}

const _doubleBezier = (options) => {
    return _loop(options, _doubleBezierTo)
}

const _symmetry = (options, fn1, fn2) => {
    return _loop(options, ([t, next], [c1, c2], index) => {
        if (index % 2 === 0) {
            return _moveTo(t) + fn1([t, next], [c1, c2])
        } else {
            return _moveTo(t) + fn2([t, next], [c1, c2])
        }
    })
}


const _symmetryBezier = (options) => {
    return _symmetry(options, _bezierTo, _leftBezierTo)
}




// 锯齿
const _sawtooth = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _sawtoothTo)
}

const _leftSawtooth = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _leftSawtoothTo)
}

const _doubleSawtooth = (options) => {
    return _loop(options, _doubleSawtoothTo)
}

const _symmetrySawtooth = (options) => {
    return _symmetry(options, _sawtoothTo, _leftSawtoothTo)
}


// 半圆
// {points, o, options = {}}
const _semicircle = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _semicircleTo)
}
const _leftSemicircle = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _leftSemicircleTo)
}



// 太极
const _taichi = (options) => {
    return _loop(options, _taichiTo)
}


// 向心弧线
export const _centripetal = (options) => {
    let { points } = options
    let o = _o(points)
    return _moveTo(points[0]) + _loop(options, ([t, next]) => _quadraticCurveTo([o, next]))

}

export const _leftCentripetal = (options) => {
    let { points } = options
    let o = _o(points)
    return _moveTo(points[0]) +
        _loop(options, ([t, next]) => {
            let c = _mid(t, next)
            let oo = _mirror(o, c)
            return _quadraticCurveTo([oo, next])
        })

}

export const _doubleCentripetal = (options) => {
    let { points } = options
    let o = _o(points)
    return _loop(options, ([t, next]) => {
        let stack = []
        stack[stack.length] = _moveTo(t)
        stack[stack.length] = _quadraticCurveTo([o, next])
        stack[stack.length] = _moveTo(t)
        let c = _mid(t, next)
        let oo = _mirror(o, c)
        stack[stack.length] = _quadraticCurveTo([oo, next])
        return stack.join(' ')

    })

}

// 菱形
const _diamond = (options) => {
    return _loop(options, _diamondTo)
}
// 十字
const _cross = (options) => {
    return _loop(options, _crossTo)
}
// 三角形
const _triangle = (options) => {
    return _loop(options, _triangleTo)
}

const _leftTriangle = (options) => {
    return _loop(options, _leftTriangleTo)
}



// 直角拐线
const _rightAngle = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _rightAngleTo)
}

const _leftAngle = (options) => {
    let { points } = options
    return _moveTo(points[0]) + _loop(options, _leftAngleTo)
}

const _symmetryAngle = (options) => {
    return _symmetry(options, _rightAngleTo, _leftAngleTo)
}

export const _polyline = (options) => {
    let { points, loop, step, discrete } = options
    // return _moveTo(points[0]) + _loop(options, _polylineTo)
    let n = step
    let iter = ([p1, p2]) => {
        return _polylineTo([p2])
    }
    let init = _moveTo

    return _traversal({
        points,
        n,
        iter,
        init,
        loop,
        discrete
    }).join(' ')
}
const _sin = (options) => {
    let { points } = options
    let ps = sinPoints(points, {})
    return _polyline({ points: ps })
}


// 互切圆
const _tangentCircle = ({ points, loop, step = 1 }) => {
    let o = _o(points)
    return _loop({ points, step, loop }, ([t, next]) => {
        let c = _mid(t, next)
        return _circleToSeg(c, [o, next])
    })
}

const _tangentTaichi = ({ points, loop, step = 1 }) => {
    let o = _o(points)
    return _loop({ points, step, loop }, ([t, next]) => {
        let c = _mid(t, next)
        return _taichiToSeg(c, [o, next])
    })
}

const _tangentMonkey = ({ points, loop, step = 1 }) => {
    let o = _o(points)
    return _loop({ points, step, loop }, ([t, next]) => {
        let c = _mid(t, next)
        return _monkeyToSeg(c, [o, next])
    })
}

// 杨辉三角
const _sierpinski = ({ points, depth, amplitude, loop, discrete }) => {
    let ps = sierpinskiPoints({ points, depth, amplitude, loop, discrete })
    return _traversal({
        points: ps,
        iter: _polylineTo,
        init: _moveTo,
        loop
    }).join(' ')

}
// 杨辉三角贝塞尔
const _sierpinskiBezier = ({ points, depth, amplitude, loop, discrete }) => {
    let ps = sierpinskiPoints({ points, depth, amplitude, loop, discrete })

    return _traversal({
        points: ps,
        iter: ([p1, p2, p3, p4]) => {
            return _bezierCurveTo([p2, p3, p4])
        },
        init: _moveTo,
        loop
    }
    ).join(' ')
}


const _sierpinskiDiscreteBezier = ({ points, depth, amplitude, loop }) => {
    let ps = sierpinskiPoints({ points, depth, amplitude, loop })
    let n = 4
    let discrete = true
    return _traversal({
        points: ps,
        n,
        iter: ([p1, p2, p3, p4]) => {
            return _moveTo(p1) + ' ' + _bezierCurveTo([p2, p3, p4])
        },
        loop,
        discrete
    }
    ).join(' ')
}

const _koch = ({ points, depth, skew, amplitude, loop, discrete }) => {
    let ps = kochPoints({ points, depth, skew, amplitude, loop, discrete })
    return _traversal({
        points: ps,
        // n,
        iter: _polylineTo,
        init: _moveTo,
        loop
    }).join(' ')
}

const _kochCurve = ({ points, depth, skew, amplitude, loop, discrete }) => {
    let ps = kochPoints({ points, depth, skew, amplitude, loop, discrete })
    let n = 3
    let iter = ([p1, p2, p3]) => {
        return _quadraticCurveTo([p2, p3])
    }
    return _traversal({
        points: ps,
        n,
        iter,
        init: _moveTo,
        loop
    }).join(' ')

}

const _kochDiscreteCurve = ({ points, depth, skew, amplitude, loop }) => {
    let ps = kochPoints({ points, depth, skew, amplitude, loop })
    let n = 5
    let iter = ([p1, p2, p3, p4, p5]) => {
        return _moveTo(p1) + ' ' + _quadraticCurveTo([p2, p3]) + _quadraticCurveTo([p4, p5])
    }
    let discrete = true
    return _traversal({
        points: ps,
        n,
        iter,
        loop,
        discrete
    }).join(' ')

}


const curveMapFn = {
    none: () => { },
    bezier: _bezier,
    leftBezier: _leftBezier,
    doubleBezier: _doubleBezier,
    symmetryBezier: _symmetryBezier,
    sawtooth: _sawtooth,
    leftSawtooth: _leftSawtooth,
    doubleSawtooth: _doubleSawtooth,
    symmetrySawtooth: _symmetrySawtooth,
    rightAngle: _rightAngle,
    leftAngle: _leftAngle,
    symmetryAngle: _symmetryAngle,
    circle: _circle,
    diamond: _diamond,
    cross: _cross,
    triangle: _triangle,
    leftTriangle: _leftTriangle,
    semicircle: _semicircle,
    leftSemicircle: _leftSemicircle,
    centripetal: _centripetal,
    leftCentripetal: _leftCentripetal,
    doubleCentripetal: _doubleCentripetal,
    none: _polyline,
    sin: _sin,
    tangentCircle: _tangentCircle,
    rough: _rough,
    taichi: _taichi,
    tangentTaichi: _tangentTaichi,
    tangentMonkey: _tangentMonkey,
    sierpinski: _sierpinski,
    sierpinskiBezier: _sierpinskiBezier,
    // sierpinskiDiscreteBezier: _sierpinskiDiscreteBezier,
    koch: _koch,
    kochCurve: _kochCurve,
    // kochDiscreteCurve: _kochDiscreteCurve
}

export const curveTypes = Object.keys(curveMapFn)

//曲线
export const _curve = ({ points, o, curve, loop, step, discrete }) => {
    let type,
        amplitude = 1,
        skew = 0,
        depth = 1
    if (isObject(curve)) {
        type = curve.type
        amplitude = curve.amplitude
        skew = curve.skew
        depth = curve.depth
    } else {
        type = curve
    }
    let fn = curveMapFn[type] || _polyline
    if (step > 1) {
        loop = true
    }
    return fn({ points, o, loop, step, skew, amplitude, depth, discrete })
}